Beheers React Suspense met praktische patronen voor efficiënt gegevens ophalen, laadstatussen en robuuste foutafhandeling. Bouw soepelere, veerkrachtigere gebruikerservaringen.
React Suspense Patroon: Gegevens ophalen en Error Boundaries
React Suspense is een krachtige functie waarmee u het renderen van componenten kunt "opschorten" terwijl u wacht op asynchrone bewerkingen, zoals het ophalen van gegevens, om te voltooien. In combinatie met Error Boundaries biedt het een robuust mechanisme voor het afhandelen van laadstatussen en fouten, wat resulteert in een soepelere en veerkrachtigere gebruikerservaring. Dit artikel onderzoekt verschillende patronen voor het effectief benutten van Suspense en Error Boundaries in uw React-toepassingen.
React Suspense begrijpen
In de kern is Suspense een mechanisme waarmee React op iets kan wachten voordat een component wordt gerenderd. Dit "iets" is doorgaans een asynchrone bewerking, zoals het ophalen van gegevens van een API. In plaats van een leeg scherm of een mogelijk misleidende tussenstaat weer te geven, kunt u een fallback UI (bijv. een laadspinner) weergeven terwijl de gegevens worden geladen.
Het belangrijkste voordeel is een verbeterde waargenomen prestatie en een aangenamere gebruikerservaring. Gebruikers krijgen onmiddellijk visuele feedback die aangeeft dat er iets gebeurt, in plaats van zich af te vragen of de applicatie is bevroren.
Belangrijkste concepten
- Suspense Component: De
<Suspense>component omhult componenten die kunnen opschorten. Het accepteert eenfallbackprop, die de UI specificeert die moet worden weergegeven terwijl de omhulde componenten zijn opgeschort. - Fallback UI: Dit is de UI die wordt weergegeven terwijl de asynchrone bewerking bezig is. Het kan van alles zijn, van een eenvoudige laadspinner tot een meer uitgebreide animatie.
- Promise Integratie: Suspense werkt met Promises. Wanneer een component probeert een waarde te lezen van een Promise die nog niet is opgelost, schort React de component op en geeft de fallback UI weer.
- Gegevensbronnen: Suspense vertrouwt op gegevensbronnen die Suspense-bewust zijn. Deze bronnen stellen een API bloot waarmee React kan detecteren wanneer gegevens worden opgehaald.
Gegevens ophalen met Suspense
Om Suspense te gebruiken voor het ophalen van gegevens, hebt u een Suspense-bewuste bibliotheek voor het ophalen van gegevens nodig. Hier is een veelvoorkomende aanpak met behulp van een aangepaste fetchData functie:
Voorbeeld: Eenvoudig gegevens ophalen
Maak eerst een utility-functie voor het ophalen van gegevens. Deze functie moet het 'opschortende' aspect afhandelen. We zullen onze fetch-aanroepen verpakken in een aangepaste resource om de promisetoestand correct af te handelen.
// utils/api.js
const wrapPromise = (promise) => {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
}
return result;
},
};
};
const fetchData = (url) => {
const promise = fetch(url)
.then((res) => res.json())
.then((data) => data);
return wrapPromise(promise);
};
export default fetchData;
Laten we nu een component maken dat Suspense gebruikt om gebruikersgegevens weer te geven:
// components/UserProfile.js
import React from 'react';
import fetchData from '../utils/api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
export default UserProfile;
Omwikkel ten slotte de UserProfile component met <Suspense>:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
function App() {
return (
<Suspense fallback={<p>Gebruikersgegevens laden...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
In dit voorbeeld probeert de UserProfile component de user gegevens van de resource te lezen. Als de gegevens nog niet beschikbaar zijn (de Promise is nog in behandeling), schort de component op en wordt de fallback UI ("Gebruikersgegevens laden...") weergegeven. Zodra de gegevens zijn opgehaald, wordt de component opnieuw weergegeven met de daadwerkelijke gebruikersinformatie.
Voordelen van deze aanpak
- Declaratief gegevens ophalen: De component drukt uit *welke* gegevens het nodig heeft, niet *hoe* het ze moet ophalen.
- Gecentraliseerd laadstatusbeheer: De Suspense-component handelt de laadstatus af, waardoor de componentlogica wordt vereenvoudigd.
Error Boundaries voor veerkracht
Hoewel Suspense laadstatussen op een elegante manier afhandelt, handelt het niet inherent fouten af die kunnen optreden tijdens het ophalen van gegevens of het renderen van componenten. Dat is waar Error Boundaries van pas komen.
Error Boundaries zijn React-componenten die JavaScript-fouten overal in hun onderliggende componentenboom opvangen, die fouten registreren en een fallback UI weergeven in plaats van de hele applicatie te laten crashen. Ze zijn cruciaal voor het bouwen van veerkrachtige UIs die onverwachte fouten op een elegante manier kunnen afhandelen.
Een Error Boundary maken
Om een Error Boundary te maken, moet u een klassecomponent definiëren die de lifecycle-methoden static getDerivedStateFromError() en componentDidCatch() implementeert.
// components/ErrorBoundary.js
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Werk de staat bij zodat de volgende render de fallback UI laat zien.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, errorInfo) {
// U kunt de fout ook registreren bij een foutrapportageservice
console.error("Caught error: ", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// U kunt elke aangepaste fallback UI renderen
return (
<div>
<h2>Er is iets misgegaan.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
De getDerivedStateFromError methode wordt aangeroepen wanneer een fout wordt gegooid in een afstammelingcomponent. Het werkt de status bij om aan te geven dat er een fout is opgetreden.
De componentDidCatch methode wordt aangeroepen nadat een fout is gegooid. Het ontvangt de fout en foutinformatie, die u kunt gebruiken om de fout te registreren bij een foutrapportageservice of om een informativere foutmelding weer te geven.
Error Boundaries gebruiken met Suspense
Om Error Boundaries te combineren met Suspense, omwikkelt u eenvoudigweg de <Suspense> component met een <ErrorBoundary> component:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
import ErrorBoundary from './components/ErrorBoundary';
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Gebruikersgegevens laden...</p>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}
export default App;
Als er nu een fout optreedt tijdens het ophalen van gegevens of het renderen van de UserProfile component, vangt de Error Boundary de fout op en geeft de fallback UI weer, waardoor de hele applicatie niet crasht.
Geavanceerde Suspense-patronen
Naast het basis ophalen van gegevens en het afhandelen van fouten, biedt Suspense verschillende geavanceerde patronen voor het bouwen van meer geavanceerde UIs.
Code Splitting met Suspense
Code splitting is het proces van het opsplitsen van uw applicatie in kleinere brokken die op aanvraag kunnen worden geladen. Dit kan de initiële laadtijd van uw applicatie aanzienlijk verbeteren.
React.lazy en Suspense maken code splitting ongelooflijk eenvoudig. U kunt React.lazy gebruiken om componenten dynamisch te importeren en ze vervolgens in te pakken met <Suspense> om een fallback UI weer te geven terwijl de componenten worden geladen.
// components/MyComponent.js
import React from 'react';
const MyComponent = React.lazy(() => import('./AnotherComponent'));
function App() {
return (
<Suspense fallback={<p>Component laden...</p>}>
<MyComponent />
</Suspense>
);
}
export default App;
In dit voorbeeld wordt de MyComponent op aanvraag geladen. Terwijl deze wordt geladen, wordt de fallback UI ("Component laden...") weergegeven. Zodra de component is geladen, wordt deze normaal gerenderd.
Parallel gegevens ophalen
Met Suspense kunt u meerdere gegevensbronnen parallel ophalen en één enkele fallback UI weergeven terwijl alle gegevens worden geladen. Dit kan handig zijn wanneer u gegevens van meerdere API's moet ophalen om één component te renderen.
import React, { Suspense } from 'react';
import fetchData from './api';
const userResource = fetchData('https://jsonplaceholder.typicode.com/users/1');
const postsResource = fetchData('https://jsonplaceholder.typicode.com/posts?userId=1');
function UserProfile() {
const user = userResource.read();
const posts = postsResource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<h3>Posts:</h3>
<ul>
{posts.map(post => (<li key={post.id}>{post.title}</li>))}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback={<p>Gebruikersgegevens en berichten laden...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
In dit voorbeeld haalt de UserProfile component zowel gebruikersgegevens als berichtgegevens parallel op. De <Suspense> component geeft één enkele fallback UI weer terwijl beide gegevensbronnen worden geladen.
Transition API met useTransition
React 18 introduceerde de useTransition hook, die Suspense verbetert door een manier te bieden om UI-updates als overgangen te beheren. Dit betekent dat u bepaalde statusupdates als minder urgent kunt markeren en kunt voorkomen dat ze de UI blokkeren. Dit is vooral handig bij het omgaan met langzamer ophalen van gegevens of complexe rendering-bewerkingen, waardoor de waargenomen prestaties worden verbeterd.
Hier is hoe u useTransition kunt gebruiken:
import React, { useState, Suspense, useTransition } from 'react';
import fetchData from './api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
function App() {
const [isPending, startTransition] = useTransition();
const [showProfile, setShowProfile] = useState(false);
const handleClick = () => {
startTransition(() => {
setShowProfile(true);
});
};
return (
<div>
<button onClick={handleClick} disabled={isPending}>
Gebruikersprofiel tonen
</button>
{isPending && <p>Laden...</p>}
<Suspense fallback={<p>Gebruikersgegevens laden...</p>}>
{showProfile && <UserProfile />}
</Suspense>
</div>
);
}
export default App;
In dit voorbeeld initieert het klikken op de knop "Gebruikersprofiel tonen" een overgang. startTransition markeert de setShowProfile-update als een overgang, waardoor React prioriteit kan geven aan andere UI-updates. De isPending-waarde van useTransition geeft aan of een overgang bezig is, zodat u visuele feedback kunt geven (bijv. de knop uitschakelen en een laadbericht weergeven).
Best practices voor het gebruik van Suspense en Error Boundaries
- Omwikkel Suspense rond het kleinst mogelijke gebied: Vermijd het omwikkelen van grote delen van uw applicatie met
<Suspense>. Omwikkel in plaats daarvan alleen de componenten die daadwerkelijk moeten opschorten. Dit minimaliseert de impact op de rest van de UI. - Gebruik zinvolle fallback UIs: De fallback UI moet gebruikers duidelijke en informatieve feedback geven over wat er gebeurt. Vermijd generieke laadspinners; probeer in plaats daarvan meer context te bieden (bijvoorbeeld "Gebruikersgegevens laden...").
- Plaats Error Boundaries strategisch: Denk goed na over waar u Error Boundaries wilt plaatsen. Plaats ze hoog genoeg in de componentenboom om fouten op te vangen die van invloed kunnen zijn op meerdere componenten, maar laag genoeg om te voorkomen dat fouten worden opgevangen die specifiek zijn voor één component.
- Registreer fouten: Gebruik de
componentDidCatch-methode om fouten te registreren bij een foutrapportageservice. Dit helpt u bij het identificeren en oplossen van fouten in uw applicatie. - Geef gebruiksvriendelijke foutmeldingen: De fallback UI die door Error Boundaries wordt weergegeven, moet gebruikers nuttige informatie geven over de fout en wat ze eraan kunnen doen. Vermijd technisch jargon; gebruik in plaats daarvan duidelijke en beknopte taal.
- Test uw Error Boundaries: Zorg ervoor dat uw Error Boundaries correct werken door opzettelijk fouten te gooien in uw applicatie.
Internationale overwegingen
Houd bij het gebruik van Suspense en Error Boundaries in internationale applicaties rekening met het volgende:
- Lokalisatie: Zorg ervoor dat de fallback UIs en foutmeldingen correct zijn gelokaliseerd voor elke taal die door uw applicatie wordt ondersteund. Gebruik internationaliserings(i18n)-bibliotheken zoals
react-intlofi18nextom vertalingen te beheren. - Right-to-left (RTL) layouts: Als uw applicatie RTL-talen (bijv. Arabisch, Hebreeuws) ondersteunt, zorg er dan voor dat de fallback UIs en foutmeldingen correct worden weergegeven in RTL-layouts. Gebruik logische CSS-eigenschappen (bijv.
margin-inline-startin plaats vanmargin-left) om zowel LTR- als RTL-layouts te ondersteunen. - Toegankelijkheid: Zorg ervoor dat de fallback UIs en foutmeldingen toegankelijk zijn voor gebruikers met een handicap. Gebruik ARIA-attributen om semantische informatie te verstrekken over de laadstatus en foutmeldingen.
- Culturele gevoeligheid: Houd rekening met culturele verschillen bij het ontwerpen van fallback UIs en foutmeldingen. Vermijd het gebruik van afbeeldingen of taal die in bepaalde culturen aanstootgevend of ongepast kunnen zijn. Een veelvoorkomende laadspinner kan bijvoorbeeld in sommige culturen negatief worden ervaren.
Voorbeeld: Gelokaliseerde foutmelding
Met react-intl kunt u gelokaliseerde foutberichten maken:
// components/ErrorBoundary.js
import React from 'react';
import { FormattedMessage } from 'react-intl';
class ErrorBoundary extends React.Component {
// ... (zelfde als voorheen)
render() {
if (this.state.hasError) {
return (
<div>
<h2><FormattedMessage id="error.title" defaultMessage="Er is iets misgegaan." /></h2>
<p><FormattedMessage id="error.message" defaultMessage="Probeer het later opnieuw." /></p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Definieer vervolgens de vertalingen in uw locale-bestanden:
// locales/en.json
{
"error.title": "Something went wrong.",
"error.message": "Please try again later."
}
// locales/fr.json
{
"error.title": "Quelque chose s'est mal passé.",
"error.message": "Veuillez réessayer plus tard."
}
Conclusie
React Suspense en Error Boundaries zijn essentiële tools voor het bouwen van moderne, veerkrachtige en gebruiksvriendelijke UIs. Door de patronen die in dit artikel worden beschreven te begrijpen en toe te passen, kunt u de waargenomen prestaties en de algehele kwaliteit van uw React-applicaties aanzienlijk verbeteren. Denk eraan om internationalisering en toegankelijkheid te overwegen om ervoor te zorgen dat uw applicaties bruikbaar zijn voor een wereldwijd publiek.
Asynchroon gegevens ophalen en correcte foutafhandeling zijn cruciale aspecten van elke webapplicatie. Suspense biedt in combinatie met Error Boundaries een declaratieve en efficiënte manier om deze complexiteit in React te beheren, wat resulteert in een soepelere en betrouwbaardere gebruikerservaring voor gebruikers over de hele wereld.